package com.mikufufu.blog.common.exception;


import com.mikufufu.blog.common.constant.HttpStatus;
import com.mikufufu.blog.common.entity.AjaxResult;
import com.mikufufu.blog.security.exception.AuthException;
import com.mikufufu.blog.utils.I18Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * 全局异常处理
 *
 * <br>
 * {@code @RestControllerAdvice}等价于{@code @ControllerAdvice}和{@code @ResponseBody}
 * {@code @ControllerAdvice}用于全局异常处理，{@code @ResponseBody}返回json数据
 * 
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 全局异常处理
     * @param request 请求对象
     * @param e 异常对象
     * @return 错误信息
     */
    @ExceptionHandler(value = {Exception.class})
    public AjaxResult<String> exceptionHandler(HttpServletRequest request, Exception e) {
        log.error("未知异常,请求地址:{}", request.getRequestURI(), e);
        return AjaxResult.error(e.getMessage());
    }

    @ExceptionHandler(value = {AuthException.class})
    public AjaxResult<String> authExceptionHandler(HttpServletRequest request, AuthException e) {
        log.error("权限异常,请求地址:{},错误信息:{}", request.getRequestURI(), e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, I18Utils.getMessage(e.getMessage()));
    }

    @ExceptionHandler(value = {NullPointerException.class})
    public AjaxResult<String> nullPointExceptionHandler(HttpServletRequest request, NullPointerException e) {
        log.error("空指针异常,请求地址:{}", request.getRequestURI(),e);
        return AjaxResult.error("空指针异常");
    }

    /**
     * 参数解析失败异常处理 如{@code @RequestBody}
     * @param e 异常对象
     * @return 错误信息
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public AjaxResult<String> handleHttpMessageNotReadableException(HttpServletRequest request,HttpMessageNotReadableException e) {
        log.error("参数解析失败异常,请求地址:{},错误信息:{}", request.getRequestURI(), e.getMessage());
        return AjaxResult.error("参数解析失败异常: " + e.getMessage());
    }

    /**
     * 参数校验异常处理
     * @param e 异常对象
     * @return 错误信息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AjaxResult<Map<String, Object>> handleValidationExceptions(HttpServletRequest request,MethodArgumentNotValidException e) {
        String requestUri = request.getRequestURI();
        log.error("参数校验失败异常, 请求地址: {}, 错误详情: {}", requestUri, e.getMessage());
        Map<String, Object> errors = new HashMap<>();
        e.getBindingResult().getAllErrors().forEach(error -> {
            String errorMessage = error.getDefaultMessage();
            String errorType = error.getClass().getSimpleName();
            Map<String, String> fieldError = new HashMap<>();
            fieldError.put("message", errorMessage);
            fieldError.put("type", errorType);
            if (error instanceof FieldError) {
                String fieldName = ((FieldError) error).getField();
                errors.put(fieldName, fieldError);
            } else {
                String objectName = error.getObjectName();
                errors.put(objectName, fieldError);
            }
        });
        return AjaxResult.error("参数校验异常处理",errors);
    }

    /**
     * 参数验证失败异常处理 如{@code @Validated}
     * @param request 请求对象
     * @param e 错误对象
     * @return 错误信息
     */
    @ExceptionHandler(value = {BindException.class, ConstraintViolationException.class})
    public AjaxResult<String> handleMethodArgumentNotValidException(HttpServletRequest request, Exception e) {
        String requestUri = request.getRequestURI();
        log.error("参数验证失败异常, 请求地址: {}, 错误详情: {}", requestUri, e.getMessage());

        String message = "未知参数错误";
        if (e instanceof BindException) {
            BindingResult bindingResult = ((BindException) e).getBindingResult();
            if (bindingResult.hasErrors()) {
                message = Optional.ofNullable(bindingResult.getFieldError())
                        .map(FieldError::getDefaultMessage)
                        .orElse("未知参数错误");
            }
        } else if (e instanceof ConstraintViolationException) {
            Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) e).getConstraintViolations();
            if (!violations.isEmpty()) {
                message =  violations.iterator().next().getMessage();
            }
        }
        return AjaxResult.error("参数验证失败异常: " + message);
    }

    /**
     * 缺少请求参数异常处理 如{@code @RequestParam}
     * @param request 请求对象
     * @param e 错误对象
     * @return 错误信息
     */
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public AjaxResult<String> missingServletRequestParameterExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException e) {
        log.error("缺少请求参数异常,请求地址:{},错误信息:{}", request.getRequestURI(), e.getMessage());
        return AjaxResult.error("缺少请求参数异常: " + e.getMessage());
    }

    /**
     * 请求方法不支持异常处理 如{@code @RequestMapping(method = RequestMethod.POST)}
     * @param request 请求对象
     * @param e 错误对象
     * @return 错误信息
     */
    @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
    public AjaxResult<String> httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        log.error("请求方法不支持异常,请求地址:{},错误信息:{}", request.getRequestURI(), e.getMessage());
        return AjaxResult.error("请求方法不支持异常: " + e.getMessage());
    }

    /**
     * 请求路径不存在异常处理
     * @param request 请求对象
     * @param e 错误对象
     * @return 错误信息
     */
    @ExceptionHandler(value = {NoHandlerFoundException.class})
    public AjaxResult<String> noHandlerFoundExceptionHandler(HttpServletRequest request, NoHandlerFoundException e) {
        log.error("请求路径不存在异常,请求地址:{},错误信息:{}", request.getRequestURI(), e.getMessage());
        return AjaxResult.error("请求路径不存在异常: " + e.getMessage());
    }

}